/*
 * MyApp.cpp
 *
 *  Created on: 2024年9月6日
 *      Author: nwz
 */
#define TAG "main"
#include <all_header.h>
#include <shellscalingapi.h>
#include <wx/wx.h>
#include <wx/String.h>
#include <wx/dataview.h>
#include <wx/aui/auibook.h>

#include <code/MyUtil.h>
#include <code/DB.h>
#include <code/DataProcess.h>
#include <code/MyApp.h>
#include <code/AttachWindow.h>
#include <code/SubWindow.h>
#include <code/EditTextDialog.h>
#include <code/CmdsWindow.h>

#include <code/MyApp.h>

wxIMPLEMENT_APP(MyApp);

static wxFrame main_frame;
static wxAuiNotebook *notebook;
static AttachWindow attach_win;
static EditTextDialog text_dialog(&main_frame);
static CmdsWindow cmds_win(&main_frame);
static DB db = DB(DATABASE_NAME);
static std::vector<struct table_tab> data_tbs;
static std::vector<struct table_menu> data_menus;
static std::vector<struct table_tab_win> data_tb_wins;
static std::vector<struct table_menu_cmd> data_menu_cmds;
static std::vector<struct table_config_int> data_config_ints;
static std::vector<SubWindow*> data_sub_windows;
static SubWindow *select_sub_window;
static wxMenu *custom_cmds_menu;

void MyApp::newWindow() {
	// get selected panel
	auto page = notebook->GetPage(notebook->GetSelection());
	for (auto tb : data_tbs) {
		if (tb.win_id == page->GetId()) {
			createWindow(page, tb, nullptr);
			return;
		}
	}
	ASSERT(0);
}

void MyApp::newTab() {
	createTab(nullptr);
}

int MyApp::createNewTabId() {
	int id = 0;
	for (auto tb : data_tbs) {
		if (tb.id > id) {
			id = tb.id;
		}
	}
	return id + 1;
}

void MyApp::createWindow(wxWindow *parent, struct table_tab &tb,
		struct table_tab_win *win) {
	auto sub = new SubWindow(attach_win);
	sub->init(parent, tb, win);
	sub->Show();
	data_sub_windows.push_back(sub);
}

void MyApp::createTab(struct table_tab *tb) {
	auto panel = new wxPanel(notebook);
	auto lay = new wxBoxSizer(wxHORIZONTAL);
	panel->SetSizer(lay);
	panel->Layout();

	if (tb == nullptr) { // add new
		struct table_tab data;
		data.id = createNewTabId();
		data.name = "new tab";
		data.win_id = panel->GetId();
		data.order_id = notebook->GetPageCount();
		notebook->AddPage(panel, data.name.c_str());
		notebook->SetSelection(data.order_id);
		data_tbs.push_back(data);
	} else {
		tb->win_id = panel->GetId();
		notebook->AddPage(panel, tb->name.c_str());
		///< create tab windows
		for (auto win : data_tb_wins) {
			if (win.tab_id == tb->id) {
				createWindow(panel, *tb, &win);
			}
		}
	}
}

void MyApp::createCustomMenu() {
	auto cnt = custom_cmds_menu->GetMenuItemCount();
	for (int i = 0; i < (int) cnt; i++) {
		custom_cmds_menu->Delete(i + CUSTOM_MENU_ID_START);
	}

	// add new menu
	cnt = CUSTOM_MENU_ID_START;
	for (auto menu : data_menus) {
		custom_cmds_menu->Append(cnt, menu.name.c_str());
		this->Bind(wxEVT_MENU, &MyApp::onCustomMenu, this, cnt);
		cnt++;
	}
}

void MyApp::createBasicMenu() {
	auto menuWindow = new wxMenu();
	menuWindow->Append(ID_MENU_WINDOW_NEW, _("&New Window\tCtrl-N"));
	menuWindow->Append(ID_MENU_WINDOW_EDIT, _("&Edit Window"));

	auto menuTab = new wxMenu();
	menuTab->Append(ID_MENU_TAB_NEW_TAB, _("&New Tab\tCtrl-T"));
	menuTab->Append(ID_MENU_TAB_EDIT_TAB, _("&Edit Tabs"));
	menuTab->AppendSeparator();
	menuTab->Append(ID_MENU_TAB_SELECT_TAB1, _("Select Tab1\tCtrl-1"));
	menuTab->Append(ID_MENU_TAB_SELECT_TAB2, _("Select Tab2\tCtrl-2"));
	menuTab->Append(ID_MENU_TAB_SELECT_TAB3, _("Select Tab3\tCtrl-3"));
	menuTab->Append(ID_MENU_TAB_SELECT_TAB4, _("Select Tab4\tCtrl-4"));
	menuTab->Append(ID_MENU_TAB_SELECT_TAB5, _("Select Tab5\tCtrl-5"));
	menuTab->Append(ID_MENU_TAB_SELECT_TAB6, _("Select Tab6\tCtrl-6"));
	menuTab->Append(ID_MENU_TAB_SELECT_TAB7, _("Select Tab7\tCtrl-7"));
	menuTab->Append(ID_MENU_TAB_SELECT_TAB8, _("Select Tab8\tCtrl-8"));
	menuTab->Append(ID_MENU_TAB_SELECT_TAB9, _("Select Tab9\tCtrl-9"));

	auto menuCommand = new wxMenu();
	menuCommand->Append(ID_MENU_COMMAND_EDIT_COMMANDS,
			_("&Edit Commands\tCtrl-E"));
	menuCommand->AppendSeparator();
	auto commands_menu = new wxMenu();
	menuCommand->AppendSubMenu(commands_menu, _("&Commands"));
	custom_cmds_menu = commands_menu;

	auto menuBar = new wxMenuBar;
	menuBar->Append(menuWindow, "&Window");
	menuBar->Append(menuTab, "&Tab");
	menuBar->Append(menuCommand, "&Command");

	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_WINDOW_NEW);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_WINDOW_EDIT);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_TAB_NEW_TAB);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_TAB_EDIT_TAB);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_TAB_SAVE_TABS);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_TAB_SELECT_TAB1);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_TAB_SELECT_TAB2);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_TAB_SELECT_TAB3);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_TAB_SELECT_TAB4);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_TAB_SELECT_TAB5);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_TAB_SELECT_TAB6);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_TAB_SELECT_TAB7);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_TAB_SELECT_TAB8);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_TAB_SELECT_TAB9);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_COMMAND_SAVE_COMMAND);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_COMMAND_EDIT_COMMANDS);
	this->Bind(wxEVT_MENU, &MyApp::onMenu, this, ID_MENU_COMMANDS);

	main_frame.SetMenuBar(menuBar);
}

void MyApp::createTabs() {
	for (auto &tab : data_tbs) {
		createTab(&tab);
	}
}

bool MyApp::OnInit() {
	// PROCESS_DPI_UNAWARE  PROCESS_SYSTEM_DPI_AWARE PROCESS_PER_MONITOR_DPI_AWARE
	::SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); ///< must set dpi first
	///
	db.init();
	db.getTbTbs(data_tbs);
	db.getTbMenus(data_menus);
	db.getTbTbWins(data_tb_wins);
	db.getTbMenuCmds(data_menu_cmds);
	db.getTbConfigInts(data_config_ints);

	main_frame.Create(NULL, wxID_ANY, _("main"));
	notebook = new wxAuiNotebook(&main_frame);
	{
		auto rect = DataProcess::getAttachWinRect(data_config_ints);
		attach_win.init(&main_frame, rect.x, rect.y, rect.width, rect.height);
	}
	auto layout = new wxBoxSizer(wxVERTICAL);
	layout->Add(notebook, 1, wxEXPAND, wxALL, 0);

	main_frame.SetSizer(layout);
	main_frame.Layout();

	createBasicMenu();
	createCustomMenu();
	createTabs();
/// connect close event for save data
	main_frame.Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(MyApp::onClose),
	NULL, this);
	auto rect = DataProcess::getWinRect(data_config_ints);
	main_frame.SetSize(rect.x, rect.y, rect.width, rect.height);
	main_frame.Show();

	text_dialog.init();
	cmds_win.init();
	return true;
}

void MyApp::onClose(wxCloseEvent&) {
	LOG_D("on close event" NL);
	db.startCommit();
// update db win_rect
	{
		auto pos = main_frame.GetPosition();
		auto size = main_frame.GetSize();
		db.updateWinRect(pos.x, pos.y, size.GetWidth(), size.GetHeight());
	}
	LOG_D("update win position success" NL);
// update db attach_win_rect
	{
		auto pos = attach_win.GetPosition();
		auto size = attach_win.GetSize();
		db.updateAttachWinRect(pos.x, pos.y, size.GetWidth(), size.GetHeight());
	}
	LOG_D("update attach win position success" NL);
	// update tbs
	{
		std::vector<struct table_tab> tbs;
		std::vector<struct table_tab_win> tb_wins;

		// find if exists
		for (auto tb : data_tbs) {
			auto tbWin = notebook->FindWindowById(tb.win_id, 0);
			if (tbWin) {
				tbs.push_back(tb);
			}
		}
		for (auto win : data_sub_windows) {
			auto win_data = *win->getWinData();
			auto find = false;
			for (auto tb : tbs) {
				if (win_data.tab_id == tb.id) {
					find = true;
					break;
				}
			}
			if (!win->isClosed() && find) {
				auto parent = notebook->GetPage(0);
				auto ppos = parent->GetScreenPosition();
				auto pos = win->GetPosition();
				auto size = win->GetClientSize();
				auto title = win->GetTitle();
				win_data.title = title;
				win_data.x = pos.x - ppos.x;
				win_data.y = pos.y - ppos.y;
				win_data.width = size.GetWidth();
				win_data.height = size.GetHeight();
				tb_wins.push_back(win_data);
			}
		}
		// reorder
		for (int i = 0; i < (int) notebook->GetPageCount(); i++) {
			for (auto &tb : tbs) {
				if (tb.win_id == notebook->GetPage(i)->GetId()) {
					tb.order_id = i;
					tb.name = notebook->GetPageText(i);
					break;
				}
			}
		}
		// database
		db.deleteTbs();
		db.deleteTbWins();
		LOG_D("delete table tbs success" NL);
		db.insertTbs(tbs);
		db.insertTbWins(tb_wins);
		LOG_D("insert data successs" NL);
	}
	LOG_D("update tabs success" NL);
	{ //  menus
		if (need_reset_db_menu) {
			need_reset_db_menu = false;
			db.deleteMenus();
			db.deleteMenuCmds();
			LOG_D("delete menus success" NL);
			db.insertMenus(data_menus);
			db.insertMenuCmds(data_menu_cmds);
			LOG_D("insert menus success" NL);
		}
	}
	db.endCommit();
	LOG_D("db commit success" NL);

	for (auto sub : data_sub_windows) {
		if (!sub->isClosed())
			sub->onClose();
		delete sub;
	}

	LOG_D("delete sub windows success" NL);
	LOG_D("close success" NL);
	main_frame.Destroy();
}

void MyApp::onMenu(wxCommandEvent &event) {
	switch (event.GetId()) {
	case ID_MENU_WINDOW_NEW:
		if (notebook->GetPageCount() == 0) {
			newTab();
		}
		newWindow();
		break;
	case ID_MENU_WINDOW_EDIT:
		if (select_sub_window && !select_sub_window->isClosed()) {
			text_dialog.SetTitle(_("Set Window Name"));
			text_dialog.setText(select_sub_window->GetTitle());
			if (text_dialog.ShowModal() == wxID_OK) {
				select_sub_window->SetTitle(text_dialog.getText());
			}
		}
		break;
	case ID_MENU_TAB_NEW_TAB:
		newTab();
		break;
	case ID_MENU_TAB_EDIT_TAB:
		if (notebook->GetSelection() > -1) {
			text_dialog.SetTitle(_("Set Tab Name"));
			text_dialog.setText(
					notebook->GetPageText(notebook->GetSelection()));
			if (text_dialog.ShowModal() == wxID_OK) {
				auto text = text_dialog.getText();
				notebook->SetPageText(notebook->GetSelection(), text);
			}
		}
		break;
	case ID_MENU_TAB_SELECT_TAB1:
	case ID_MENU_TAB_SELECT_TAB2:
	case ID_MENU_TAB_SELECT_TAB3:
	case ID_MENU_TAB_SELECT_TAB4:
	case ID_MENU_TAB_SELECT_TAB5:
	case ID_MENU_TAB_SELECT_TAB6:
	case ID_MENU_TAB_SELECT_TAB7:
	case ID_MENU_TAB_SELECT_TAB8:
	case ID_MENU_TAB_SELECT_TAB9:
		if ((int) notebook->GetPageCount()
				> event.GetId() - ID_MENU_TAB_SELECT_TAB1) {
			notebook->SetSelection(event.GetId() - ID_MENU_TAB_SELECT_TAB1);
		}
		break;
	case ID_MENU_COMMAND_EDIT_COMMANDS:
		cmds_win.reset(data_menus, data_menu_cmds);
		if (cmds_win.ShowModal() == wxID_OK) {
			data_menus = cmds_win.getMenus();
			data_menu_cmds = cmds_win.getMenuCmds();
			need_reset_db_menu = true;

			createCustomMenu();
		}
		break;
	}
}

void MyApp::onCustomMenu(wxCommandEvent &event) {
	auto id = event.GetId();
	if (id > -1) {
		auto menu_id = data_menus[id - CUSTOM_MENU_ID_START].id;
		for (auto cmd : data_menu_cmds) {
			if (cmd.menu_id == menu_id) {
				// create process
				char app[1024];
				snprintf(app, sizeof(app), "%s %s", cmd.process_name.c_str(),
						cmd.process_arg.c_str());
				LOG_D("execute %s" NL, app);
				STARTUPINFOA lsf;
				PROCESS_INFORMATION lpf;
				MEM_ZERO(lsf);
				MEM_ZERO(lpf);
				::CreateProcessA(NULL, app, NULL, NULL,
				FALSE, CREATE_NEW_CONSOLE, nullptr,
				NULL, &lsf, &lpf);
			}
		}
	}
}

int MyApp::OnExit() {
	return 0;
}

void MyApp::setActiveSubWindow(wxWindow *win) {
	select_sub_window = (SubWindow*) win;
}

